package com.blink.jtblc.core.serialized;

import com.blink.jtblc.core.fields.Field;
import com.blink.jtblc.encoding.common.B16;

public class BinaryParser {
	protected final int size;
	protected byte[] bytes;
	protected int cursor = 0;
	
	public BinaryParser(byte[] bytes) {
		this.size = bytes.length;
		this.bytes = bytes;
	}
	
	public BinaryParser(int size) {
		this.size = size;
	}
	
	public BinaryParser(String hex) {
		this(B16.decode(hex));
	}
	
	public void skip(int n) {
		cursor += n;
	}
	
	public byte readOne() {
		return bytes[cursor++];
	}
	
	public byte[] read(int n) {
		byte[] ret = new byte[n];
		System.arraycopy(bytes, cursor, ret, 0, n);
		cursor += n;
		// }
		return ret;
	}
	
	public Field readField() {
		int fieldCode = readFieldCode();
		Field field = Field.fromCode(fieldCode);
		if (field == null) {
			throw new IllegalStateException("Couldn't parse field from " + Integer.toHexString(fieldCode));
		}
		return field;
	}
	
	public int readFieldCode() {
		byte tagByte = readOne();
		int typeBits = (tagByte & 0xFF) >>> 4;
		if (typeBits == 0) {
			typeBits = readOne();
		}
		int fieldBits = tagByte & 0x0F;
		if (fieldBits == 0) {
			fieldBits = readOne();
		}
		return (typeBits << 16 | fieldBits);
	}
	
	public boolean end() {
		return cursor >= size; // greater guard against infinite loops
	}
	
	public int pos() {
		return cursor;
	}
	
	public int readOneInt() {
		return readOne() & 0xFF;
	}
	
	public int readVLLength() {
		int b1 = readOneInt();
		int result;
		if (b1 <= 192) {
			result = b1;
		} else if (b1 <= 240) {
			int b2 = readOneInt();
			result = 193 + (b1 - 193) * 256 + b2;
		} else if (b1 <= 254) {
			int b2 = readOneInt();
			int b3 = readOneInt();
			result = 12481 + (b1 - 241) * 65536 + b2 * 256 + b3;
		} else {
			throw new RuntimeException("Invalid varint length indicator");
		}
		return result;
	}
	
	public int size() {
		return size;
	}
	
	public boolean end(Integer customEnd) {
		return cursor >= size || customEnd != null && cursor >= customEnd;
	}
}
